package com.gmcloud.upms.biz.service.impl;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gmcloud.common.core.constant.enums.MenuTypeEnum;
import com.gmcloud.common.core.exception.BaseException;
import com.gmcloud.common.mybatis.base.BasePageEntity;
import com.gmcloud.common.mybatis.plus.QueryWrapperPlus;
import com.gmcloud.common.utils.BeanUtil;
import com.gmcloud.common.utils.Builder;
import com.gmcloud.upms.api.system.entity.dto.*;
import com.gmcloud.upms.api.system.entity.SysMenu;
import com.gmcloud.upms.api.system.entity.SysPost;
import com.gmcloud.upms.api.system.entity.SysRole;
import com.gmcloud.upms.api.system.entity.SysUser;
import com.gmcloud.upms.api.system.entity.payload.UserQueryCriteria;
import com.gmcloud.upms.api.system.entity.vo.*;
import com.gmcloud.upms.biz.mapper.SysUserMapper;
import com.gmcloud.upms.biz.service.*;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zl.sir
 * @version 1.0
 * @since 2022/8/26 14:15
 * 用户操作实现类
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    private final SysRoleService sysRoleService;

    private final SysPostService sysPostService;

    private final SysMenuService sysMenuService;

    private final SysOrganizationService organizationService;

    private final BCryptPasswordEncoder encoding;


    public SysUserServiceImpl(SysRoleService sysRoleService, SysPostService sysPostService, SysMenuService sysMenuService, SysOrganizationService organizationService, BCryptPasswordEncoder encoding) {
        this.sysRoleService = sysRoleService;
        this.sysPostService = sysPostService;
        this.sysMenuService = sysMenuService;
        this.organizationService = organizationService;
        this.encoding = encoding;
    }

    @Override
    public UserInfo findUserInfo(SysUser sysUser) {
        // 获取角色列表
        List<SysRole> roleList = sysRoleService.findListByUserId(sysUser.getId());
        // 设置角色列表 （ID）
        List<Long> roleIds = roleList.stream().map(SysRole::getId).toList();
        // 设置岗位列表
        List<SysPost> postList = sysPostService.listByUserId(sysUser.getId());

        // 设置权限列表（menu.permission）
        Set<String> permissions = roleIds.stream().map(sysMenuService::setByRoleId).flatMap(Collection::stream)
                .filter(m -> Objects.equals(MenuTypeEnum.BUTTON.getType(), m.getType().intValue())).map(SysMenu::getPermission)
                .filter(StrUtil::isNotBlank).collect(Collectors.toSet());

        return Builder.of(UserInfo::new)
                .with(UserInfo::setSysUser, sysUser)
                .with(UserInfo::setRoleList, roleList)
                .with(UserInfo::setRoles, ArrayUtil.toArray(roleIds, Long.class))
                .with(UserInfo::setPostList, postList)
                .with(UserInfo::setPermissions, ArrayUtil.toArray(permissions, String.class))
                .build();
    }

    @Override
    public BasePageEntity<SysUserVo> page(IPage<SysUser> page, UserQueryCriteria queryCriteria) {
        Wrapper<SysUser> wrapper = QueryWrapperPlus.getPredicate(queryCriteria, SysUser::new);
        IPage<SysUser> iPage = baseMapper.selectPage(page, wrapper);
        List<SysUser> sysUsers = iPage.getRecords();

        List<SysUserVo> sysUserVos = BeanUtil.copyListProperties(sysUsers, SysUserVo::new);
        if (iPage.getSize() > 0) {
            List<Long> userIds = sysUsers.stream().map(SysUser::getId).toList();
            Map<Long, List<SysRoleVo>> sysRoleMap = sysRoleService.findMapByUserIds(userIds);
            Map<Long, List<SysOrganizationVo>> organizationMap = organizationService.findMapByUserIds(userIds);
            Map<Long, List<SysPostVo>> positionMap = sysPostService.mapByUserIds(userIds);

            sysUserVos.parallelStream().forEach(sysUserVo -> {
                sysUserVo.setRoles(sysRoleMap.get(sysUserVo.getId()));
                sysUserVo.setOrganizations(organizationMap.get(sysUserVo.getId()));
                sysUserVo.setPosts(positionMap.get(sysUserVo.getId()));
            });

        }
        return new BasePageEntity<>(sysUserVos, iPage.getTotal());
    }

    @Transactional(rollbackFor = BaseException.class)
    @Override
    public void save(SysUserDto sysUserDto) {
        SysUser sysUser = new SysUser();
        sysUserDto.setPassword(encoding.encode(Optional.ofNullable(sysUserDto.getPassword()).orElse("123456")));
        BeanUtil.copyNotEmptyProperties(sysUserDto, sysUser);
        baseMapper.insert(sysUser);

        if (!CollectionUtils.isEmpty(sysUserDto.getRoles())) {
            baseMapper.insertUserAndRoles(sysUser.getId(), sysUserDto.getRoles().stream().map(SysRoleDto::getId).toList());
        }
        if (!CollectionUtils.isEmpty(sysUserDto.getOrganizations())) {
            baseMapper.insertUserAndOrganizations(sysUser.getId(), sysUserDto.getOrganizations().stream().map(SysOrganizationDto::getId).toList());
        }
        if (!CollectionUtils.isEmpty(sysUserDto.getPosts())) {
            baseMapper.insertUserAndPosts(sysUser.getId(), sysUserDto.getPosts().stream().map(SysPostDto::getId).toList());
        }
    }

    @Transactional(rollbackFor = BaseException.class)
    @Override
    public void update(SysUserDto sysUserDto) {
        SysUser sysUser = new SysUser();
        BeanUtil.copyNotEmptyProperties(sysUserDto, sysUser);

        if (!CollectionUtils.isEmpty(sysUserDto.getRoles())) {
            baseMapper.deleteUserAndRoles(sysUser.getId());
            baseMapper.insertUserAndRoles(sysUser.getId(), sysUserDto.getRoles().stream().map(SysRoleDto::getId).toList());
        }
        if (!CollectionUtils.isEmpty(sysUserDto.getOrganizations())) {
            baseMapper.deleteUserAndOrganizations(sysUser.getId());
            baseMapper.insertUserAndOrganizations(sysUser.getId(), sysUserDto.getOrganizations().stream().map(SysOrganizationDto::getId).toList());
        }
        if (!CollectionUtils.isEmpty(sysUserDto.getPosts())) {
            baseMapper.deleteUserAndPosts(sysUser.getId());
            baseMapper.insertUserAndPosts(sysUser.getId(), sysUserDto.getPosts().stream().map(SysPostDto::getId).toList());
        }
        baseMapper.updateById(sysUser);
    }

    @Override
    public void delete(SysUserDto sysUserDto) {
        baseMapper.deleteById(sysUserDto.getId());
    }
}
